package com.jarveis.frame.io;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;

import org.apache.log4j.Logger;

import com.jarveis.frame.lang.CharacterUtil;
import com.jarveis.frame.lang.StringUtil;

/**
 * Http工具类
 * 
 * @author liuguojun
 * @date 2014-07-31
 */
public final class HttpUtil {

	private static final Logger logger = Logger.getLogger(HttpUtil.class);

	public static String DEFAULT_CHARSET = "UTF8";
	public static int DEFAULT_TIMEOUT = 5000;

	public static String PROTOCOL_HTTP = "HTTP";
	public static String PROTOCOL_HTTPS = "HTTPS";

	public static String HEADER_USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36 LBBROWSER";
	public static String HEADER_ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
	public static String HEADER_ACCEPT_LANGUAGE = "zh-cn,zh;q=0.8";
	public static String HEADER_ACCEPT_CHARSET = "ISO-8859-1,utf-8;q=0.7,*;q=0.7";

	public static enum HttpStatus {
		/**
		 * 打开
		 */
		Opened,
		/**
		 * 关闭
		 */
		Closed
	}

	public static enum HttpResult {
		/**
		 * 成功
		 */
		Success,

		/**
		 * 超时
		 */
		TimeOut,

		/**
		 * URL错误
		 */
		Malform,

		/**
		 * IO错误
		 */
		IOError,

		/**
		 * 未知错误
		 */
		UKError,

		/**
		 * 连接未断开
		 */
		Conflict
	}

	public static enum HttpMethod {
		/**
		 * Post请求
		 */
		POST,

		/**
		 * Gets请求
		 */
		GET
	}

	public static interface HttpListener {
		void onResponse(HttpUtil http);
	}

	private int timeout;
	private String httpURL;
	private String charSet;
	private HttpMethod method;
	private HttpStatus status;
	private HttpResult result;
	private int respCode;
	private String respMessage;
	private byte[] respData;
	private boolean ignoreContent = false;
	private HashMap<String, String> params;

	public HttpUtil(String httpURL) {
		this(httpURL, HttpMethod.POST);
	}

	public HttpUtil(String httpURL, HttpMethod method) {
		this(httpURL, method, CharacterUtil.UTF8);
	}

	public HttpUtil(String httpURL, HttpMethod method, String charSet) {
		this.method = method;
		this.httpURL = httpURL;
		this.charSet = charSet;
		this.params = new HashMap<String, String>();
		this.status = HttpStatus.Closed;
	}

	public HttpMethod getMethod() {
		return method;
	}

	public void setMethod(HttpMethod method) {
		this.method = method;
	}

	public HttpStatus getStatus() {
		return status;
	}

	public HttpResult getResult() {
		return result;
	}

	public byte[] getRespData() {
		if (ignoreContent) {
			throw new RuntimeException("本连接设置了忽略内容选项");
		}
		return respData;
	}

	public int getRespCode() {
		return respCode;
	}

	public String getRespMessage() {
		return respMessage;
	}

	public void setIgnoreContent(boolean ignore) {
		this.ignoreContent = ignore;
	}

	/**
	 * 设置请求编码
	 * 
	 * @param charset
	 */
	public void setCharset(String charset) {
		this.charSet = charset;
	}

	/**
	 * 清空参数
	 */
	public void clearParams() {
		params.clear();
	}

	/**
	 * 添加参数
	 * 
	 * @param param
	 * @param value
	 */
	public void addParam(String param, String value) {
		params.put(param, value);
	}

	/**
	 * 添加参数
	 * 
	 * @param param
	 * @param value
	 */
	public void addParam(String param, Object value) {
		params.put(param, value.toString());
	}

	/**
	 * 执行请求
	 * 
	 * @return
	 */
	public boolean doRequest() {
		return doRequest(5000);
	}

	/**
	 * 执行请求
	 * 
	 * @param timeout
	 * @return
	 */
	public boolean doRequest(final int timeout) {
		if (status != HttpStatus.Closed) {
			result = HttpResult.Conflict;
			return false;
		}

		status = HttpStatus.Opened;
		this.timeout = timeout;
		try {
			if (method == HttpMethod.POST) {
				this.doPost();
			} else {
				this.doGet();
			}
		} catch (MalformedURLException ex) {
			result = HttpResult.Malform;
		} catch (IOException ex) {
			result = HttpResult.IOError;
		} catch (Throwable th) {
			result = HttpResult.UKError;
		}
		status = HttpStatus.Closed;

		return result == HttpResult.Success;
	}

	/**
	 * 执行请求
	 * 
	 * @param listener
	 * @return
	 */
	public boolean doRequest(final HttpListener listener) {
		return doRequest(5000, listener);
	}

	/**
	 * 执行请求
	 * 
	 * @param timeout
	 * @param listener
	 * @return
	 */
	public boolean doRequest(final int timeout, final HttpListener listener) {
		if (listener == null) {
			result = HttpResult.UKError;
			return false;
		}

		if (status != HttpStatus.Closed) {
			result = HttpResult.Conflict;
			return false;
		}

		this.timeout = timeout;
		final HttpUtil _self = this;
		Thread thread = new Thread() {
			public void run() {
				status = HttpStatus.Opened;
				try {
					if (method == HttpMethod.POST) {
						_self.doPost();
					} else {
						_self.doGet();
					}
				} catch (MalformedURLException ex) {
					result = HttpResult.Malform;
				} catch (IOException ex) {
					result = HttpResult.IOError;
				} catch (Throwable th) {
					result = HttpResult.UKError;
				}

				status = HttpStatus.Closed;

				listener.onResponse(HttpUtil.this);
			}
		};

		thread.start();

		return true;
	}

	/**
	 * 获取网络连接
	 * 
	 * @param url
	 * @return
	 * @throws Exception
	 */
	private HttpURLConnection getConn(String url) throws Exception {
		return this.getConn(new URL(url));
	}

	/**
	 * 获取网络连接
	 * 
	 * @param url
	 * @return
	 * @throws Exception
	 */
	private HttpURLConnection getConn(URL url) throws Exception {
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestProperty("User-Agent", HEADER_USER_AGENT);
		conn.setRequestProperty("Accept", HEADER_ACCEPT);
		conn.setRequestProperty("Accept-Language", HEADER_ACCEPT_LANGUAGE);
		conn.setRequestProperty("Accept-Charset", HEADER_ACCEPT_CHARSET);
		// 连接主机的超时时间
		conn.setConnectTimeout(timeout);
		// 从主机读取数据的超时时间
		conn.setReadTimeout(timeout);
		conn.setDoInput(true);
		conn.setDoOutput(false);
		conn.setAllowUserInteraction(false);

		return conn;
	}

	/**
	 * 执行get方法
	 * 
	 * @throws Exception
	 */
	private void doGet() throws Exception {
		HttpURLConnection conn = null;

		try {
			String paramStr = getParamsString();
			String getHttpURL = httpURL;
			if (!StringUtil.isEmpty(paramStr)) {
				if (getHttpURL.indexOf("?") > 0) {
					getHttpURL += "&" + paramStr;
				} else {
					getHttpURL += "?" + paramStr;
				}
			}
			conn = getConn(getHttpURL);
			logger.info("get:" + getHttpURL);

			respCode = conn.getResponseCode();
			respMessage = conn.getResponseMessage();
			if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
				if (!ignoreContent) {
					respData = readData(conn.getInputStream());
					logger.info("get-Resp:" + respData);
				}
				result = HttpResult.Success;
			} else {
				result = HttpResult.UKError;
			}
		} finally {
			if (conn != null) {
				conn.disconnect();
			}
		}
	}

	/**
	 * 执行post方法
	 * 
	 * @throws Exception
	 */
	private void doPost() throws Exception {
		HttpURLConnection conn = null;

		try {
			String paramStr = getParamsString();
			conn = getConn(httpURL);
			conn.setDoOutput(true);

			DataOutputStream posts = new DataOutputStream(
					conn.getOutputStream());
			posts.writeBytes(paramStr);
			posts.flush();
			posts.close();
			logger.info("post:" + httpURL);
			logger.info("post-params:" + paramStr);

			respCode = conn.getResponseCode();
			respMessage = conn.getResponseMessage();
			if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
				if (!ignoreContent) {
					respData = readData(conn.getInputStream());
					logger.info("post-Resp:" + respData);
				}
				result = HttpResult.Success;
			} else {
				result = HttpResult.UKError;
			}
		} finally {
			if (conn != null) {
				conn.disconnect();
			}
		}

	}

	/**
	 * 读取返回数据
	 * 
	 * @param stream
	 * @return
	 * @throws Exception
	 * @throws Error
	 */
	private byte[] readData(InputStream stream) throws Exception, Error {
		BufferedInputStream bis = null;
		ByteArrayOutputStream baos = null;
		try {
			bis = new BufferedInputStream(stream);
			baos = new ByteArrayOutputStream();
			int c;
			while ((c = bis.read()) != -1) {
				baos.write(c);
			}
		} finally {
			if (baos != null) {
				baos.close();
			}
			if (bis != null) {
				bis.close();
			}
			if (stream != null) {
				stream.close();
			}
		}

		return baos.toByteArray();
	}

	/**
	 * 构建参数字符串
	 * 
	 * @return
	 */
	private String getParamsString() {
		StringBuffer buffer = new StringBuffer();

		if (method == HttpMethod.POST) {
			for (String param : params.keySet()) {
				try {
					buffer.append("&");
					if (!"_inputstream".equals(param)) {
						buffer.append(URLEncoder.encode(param, charSet));
						buffer.append("=");
					}
					buffer.append(URLEncoder.encode(params.get(param), charSet));
				} catch (Exception ex) {

				}
			}
		} else if (method == HttpMethod.GET) {
			for (String param : params.keySet()) {
				buffer.append("&");
				buffer.append(param);
				buffer.append("=");
				buffer.append(params.get(param));
			}
		}
		return buffer.length() > 0 ? buffer.substring(1) : "";
	}

	public static void main(String[] args) {
		HttpUtil httpUtil = new HttpUtil("http://www.56daily.com",
				HttpMethod.GET);
		if (httpUtil.doRequest()) {
			System.out.println(new String(httpUtil.getRespData()));
		}
	}
}
